package mt3070;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

//线段树节点
class Tree {
	public int l; // 节点表示区间的左界
	public int r; // 节点表示区间的右届
	public int val; // 节点数据（0的数量）
	public int lz; // lazy标记，0表示未设置
	public Tree(int l, int r) {
		this.l = l;
		this.r = r;
	}
}

public class Main {
	public static int N = 1000007;
	public static Tree[] t = new Tree[4 * N];
	public static int lc(int p) {
		// 左儿子编号p*2
		return p << 1; // 位运算提高效率
	}
	public static int rc(int p) {
		// 右儿子编号p*2+1
		return p << 1 | 1; // 位运算提高效率
	}
	public static void build(int p, int l, int r) {
		// 实例化节点
		t[p] = new Tree(l, r);
		// l==r则当前节点为叶子，直接存值
		// 叶子节点的l对应原数据的下标
		if (l == r) {
			t[p].val = 1; // 叶子节点初始对应0，0的数量为1
			return;
		}
		// 折半找区间[l,r]的中点
		int mid = (l + r) >> 1;
		// 左儿子递归建树
		build(lc(p), l, mid);
		// 右儿子递归建树
		build(rc(p), mid + 1, r);
		// 从下往上传递区间值，放入节点
		// 此处决定线段树维护区间和或最大值或最小值
		pushUp(p);
	}

	// 从下往上传递区间值，放入节点
	// 此处的p是叶子节点
	private static void pushUp(int p) {
		// 左子树0的数量加右子树0的数量
		t[p].val = t[lc(p)].val + t[rc(p)].val;
	}
	// 区间更新、查询与懒操作部分代码
	// 设置lazy标记
	// 参数p节点编号
	// 参数v，每个子节点修改后的值
	public static void lazy(int p) {
		// 取反操作，节点新值为：左右子树长度减去左右子树0的数量
		t[p].val = (t[p].r - t[p].l + 1) - t[p].val;
		
		// 设置懒标记为1
		// 用异或操作是因为该区间如果两次取反
		// 相当于区间内不用做任何操作
		// 0^1==1,  1^1==0
		t[p].lz ^= 1;
	}
	// 向下传递懒标记
	// 参数p节点编号
	public static void pushDown(int p) {
		lazy(lc(p)); // 下传左子节点
		lazy(rc(p)); // 下传右子节点
		// 清除自身lazy标记（还原为0）
		t[p].lz = 0;
	}
	// 区间更新（lazy标记）
	// 参数p节点编号
	// 参数l，r表示将[l,r]区间更新为v
	public static void update(int p, int l, int r) {
		// 找到了[l,r]子区间节点
		if (t[p].l >= l && t[p].r <= r) {
			// 更新节点值并设置lazy
			lazy(p);
			return;
		}
		// [l,r]和节点表示区间有交集的情况
		// 让节点的左右子树去处理
		// 当前节点值的更新是在本方法最后一行回溯时做的
		if (t[p].lz == 1 && t[p].l != t[p].r) {
			// 如果当前节点有lazy标记则下传给子树
			pushDown(p);
		}
		// 折半找区间[t[p].l,t[p].r]的中点
		int mid = (t[p].l + t[p].r) >> 1;
		if (l <= mid)
			update(lc(p), l, r); // 左子树更新
		if (r > mid)
			update(rc(p), l, r); // 右子树更新
		// 从下往上更新区间值，放入节点
		// 此处决定线段树维护区间和或最大值或最小值
		pushUp(p);
	}
	// 查询线段树（带lazy标记的情况）
	// 参数p节点编号
	// 参数l，r表示求解的（区间和或最大值或最小值）区间[l,r]
	public static int queryl(int p, int l, int r) {
		// 1、查询区间覆盖节点区间，返回节点值（区简解）
		if (t[p].l >= l && t[p].r <= r) {
			return t[p].val;
		}
		// [l,r]和节点表示区间有交集的情况
		// 让节点的左右子树去处理
		if (t[p].lz == 1) {
			// 如果当前节点有lazy标记则下传给子树
			pushDown(p);
		}
		// 折半找节点区间[t[p].l,t[p].r]的中点
		int mid = (t[p].l + t[p].r) >> 1;
		int ans = 0;
		// 查询区间和的情况
		// l与左子树有重叠，则进入左子树递归查询
		if (l <= mid)
			ans += queryl(lc(p), l, r);
		// r与右子树有重叠，则进入左子树递归查询
		if (r > mid)
			ans += queryl(rc(p), l, r);
		return ans;
	}

	// 输入优化模板开始
	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static StreamTokenizer st = new StreamTokenizer(br);
	static int rd() throws IOException {
		st.nextToken();
		return (int) st.nval;
	}
	// 输入优化模板结束
	// 输出优化
	static StringBuilder sb = new StringBuilder();
	public static void main(String[] args) throws IOException {
		int n,m;
		n = rd();
		m = rd();
		build(1,1,n);
		for(int i=0;i<m;i++) {
			int x = rd();
			int l = rd();
			int r = rd();
			if(x==0) {
				update(1,l,r);
			}else {
				sb.append(queryl(1,l,r));
				sb.append("\n");
			}
		}
		System.out.print(sb.toString());
	}
}
